package com.mzj.saas.commons.util;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;

public class AsciiStringTokenizer
{
	private static final int TAILOR = -1 ;

	ByteBuffer buffer ;
	int offset ;
	byte delim ;
	
	int[] delimIdx ;
	String[] updatedFields ;

	public AsciiStringTokenizer( ByteBuffer buffer, char delim )
	{
		this( buffer, delim, Integer.MAX_VALUE ) ;
	}
	
	public AsciiStringTokenizer( ByteBuffer buffer, int offset, char delim )
	{
		this( buffer, offset, delim, Integer.MAX_VALUE ) ;
	}

	public AsciiStringTokenizer( ByteBuffer buffer, char delim, int fieldNumLimit )
	{
		this( buffer, (byte)delim, fieldNumLimit ) ;
	}

	public AsciiStringTokenizer( ByteBuffer buffer, int offset, char delim, int fieldNumLimit )
	{
		this( buffer, offset, (byte)delim, fieldNumLimit ) ;
	}
	
	public AsciiStringTokenizer( ByteBuffer buffer, byte delim )
	{
		this( buffer, delim, Integer.MAX_VALUE ) ;
	}

	public AsciiStringTokenizer( ByteBuffer buffer, int offset, byte delim )
	{
		this( buffer, offset, delim, Integer.MAX_VALUE ) ;
	}

	public AsciiStringTokenizer( ByteBuffer buffer, byte delim, int fieldNumLimit )
	{
		this( buffer, buffer.position(), delim, fieldNumLimit ) ;
	}

	public AsciiStringTokenizer( ByteBuffer buffer, int offset, byte delim, int fieldNumLimit )
	{
		assert fieldNumLimit>0 ;
		
		this.buffer = buffer ;
		this.offset = offset ;
		this.delim = delim ;

		if( fieldNumLimit > 1 )
		{
			buffer.position( offset ) ;
			ArrayList<Integer> arr = new ArrayList<Integer>(64) ;
			int fieldNum = 1 ;
			while(buffer.hasRemaining())
			{
				if( delim == buffer.get() )
				{
					arr.add( buffer.position()-1 ) ;
					fieldNum ++ ;

					if( fieldNum >= fieldNumLimit )
						break ;
				}
			}
			
			arr.add( TAILOR ) ;

			delimIdx = new int[arr.size()] ;
			Iterator<Integer> iter = arr.iterator() ;
			int count=0 ;
			while( iter.hasNext() )
				delimIdx[count++] = iter.next() ;
		}
		else
		{
			delimIdx = new int[]{TAILOR} ;
		}
	}
	
	public int getFieldCount()
	{
		return delimIdx.length ;
	}
	
	public int getDelimIdx( int index )
	{
		if( index < 0 || index >= delimIdx.length )
			throw new IllegalArgumentException( "Invalid delim index." ) ;
		return delimIdx[index] ;		
	}
	
	public int getFieldAsInt( int index )
	{
		String val = getField( index ) ;
		if( val == null )
			return 0 ;
		else
			return ConvertUtils.str2int(val) ;
	}
	
	public long getFieldAsLong( int index )
	{
		String val = getField( index ) ;
		if( val == null )
			return 0 ;
		else
			return ConvertUtils.str2long(val) ;
	}
	
	public String getField( int index )
	{
		if( index < 0 || index >= delimIdx.length )
			throw new IllegalArgumentException( "Invalid field index." ) ;
		
		if( updatedFields != null && updatedFields[index] != null )
			return updatedFields[index] ;
		
		if( index == 0 )
			buffer.position(offset) ;
		else
			buffer.position(delimIdx[index-1]+1);
		
		int delimPos = delimIdx[index] ;
		if( delimPos == TAILOR )
			return ByteUtils.readString(buffer) ;
		else
		{
			int len = delimPos-buffer.position() ;
			if( len == 0 )
				return null ;
			return ByteUtils.readString(buffer,len ) ;
		}
	}
	
	public void setField( int index, int val )
	{
		setField( index, String.valueOf(val) ) ;
	}
	
	public void setField( int index, long val )
	{
		setField( index, String.valueOf(val) ) ;
	}
	
	public void setField( int index, String str )
	{
		if( index < 0 || index >= delimIdx.length )
			throw new IllegalArgumentException( "Invalid field index." ) ;

		if( updatedFields == null )
			updatedFields = new String[delimIdx.length] ;
		updatedFields[index] = str ;
	}
	
	public boolean hasChanged()
	{
		return updatedFields!=null ;
	}
	
	public int size()
	{
		buffer.position(offset) ;
		int estimateSize = buffer.remaining() ;
		for( int i=0; i<updatedFields.length; i++ )
		{
			String str = updatedFields[i] ;
			if( str != null )
				estimateSize += str.length() ;
		}
		return estimateSize ;
	}
	
	public void getContent( ByteBuffer newbuf )
	{
		buffer.position(offset) ;
		ByteBuffer origin = buffer.asReadOnlyBuffer() ;

		if( updatedFields != null )
		{
			int originLimit = origin.limit() ;
			for( int i=0; i<updatedFields.length; i++ )
			{
				String str = updatedFields[i] ;
				if( str == null )
					continue ;
				
				if( i>0 )
				{
					int copyEndPos = delimIdx[i-1]+1 ;
					if( copyEndPos>origin.position() )
					{
						origin.limit( copyEndPos ) ;
						newbuf.put( origin ) ;
						origin.limit( originLimit ) ;
					}
				}
				
				newbuf.put( ByteUtils.str2bytes( str, "UTF-8" ) ) ;
	
				if( delimIdx[i] >= 0 )
					origin.position( delimIdx[i] ) ;
				else
					origin.position( origin.limit() ) ;
			}
		}
		
		if( origin.hasRemaining() )
			newbuf.put( origin ) ;
		
		newbuf.flip() ;
	}
	
	public ByteBuffer getContent()
	{
		int size = size() ;
		ByteBuffer newbuf =  buffer.isDirect()?ByteBuffer.allocateDirect( size ):ByteBuffer.allocate( size ) ;
		
		getContent( newbuf ) ;
		return newbuf ;
	}
}

